package com.qianfeng.ssmplatform.web.service.impl;


//
//                            _ooOoo_  
//                           o8888888o  
//                           88" . "88  
//                           (| -_- |)  
//                            O\ = /O  
//                        ____/`---'\____  
//                      .   ' \\| |// `.  
//                       / \\||| : |||// \  
//                     / _||||| -:- |||||- \  
//                       | | \\\ - /// | |  
//                     | \_| ''\---/'' | |  
//                      \ .-\__ `-` ___/-. /  
//                   ___`. .' /--.--\ `. . __  
//                ."" '< `.___\_<|>_/___.' >'"".  
//               | | : `- \`.;`\ _ /`;.`/ - ` : | |  
//                 \ \ `-. \_ __\ /__ _/ .-` / /  
//         ======`-.____`-.___\_____/___.-`____.-'======  
//                            `=---='  
//  
//         .............................................  
//                  佛祖镇楼            BUG辟易  
//          佛曰:  
//                  写字楼里写字间，写字间里程序员；  
//                  程序人员写程序，又拿程序换酒钱。  
//                  酒醒只在网上坐，酒醉还来网下眠；  
//                  酒醉酒醒日复日，网上网下年复年。  
//                  但愿老死电脑间，不愿鞠躬老板前；  
//                  奔驰宝马贵者趣，公交自行程序员。  
//                  别人笑我忒疯癫，我笑自己命太贱；  


import com.qianfeng.smsplatform.common.constants.CacheConstants;
import com.qianfeng.smsplatform.common.constants.InterfaceExceptionDict;
import com.qianfeng.smsplatform.common.model.Standard_Submit;
import com.qianfeng.ssmplatform.web.exception.MyBaseException;
import com.qianfeng.ssmplatform.web.exception.MyBaseExceptionWithResult;
import com.qianfeng.ssmplatform.web.service.IPushSubmitQueueService;
import com.qianfeng.ssmplatform.web.service.ISmsCheckService;
import com.qianfeng.ssmplatform.web.service.api.CacheService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * Created by jackiechan on 2021/3/26 21:33
 *
 * @author jackiechan
 */
@Service
public class SmsCheckServiceImpl implements ISmsCheckService {
    static final Logger LOGGER = LoggerFactory.getLogger(SmsCheckServiceImpl.class);

    @Autowired
    private CacheService cacheService;

    @Autowired
    private IPushSubmitQueueService pushSubmitQueueService;

    @Override
    public void checkSms(Map map) {

        System.err.println("开始检查了" + map);
        //我们客户的接入信息保存在了缓存中
        String clientID = (String) map.get("clientID");
        String srcId = (String) map.get("srcId");
        Map<Object, Object> clientInfo = cacheService.hGetAll(CacheConstants.CACHE_PREFIX_CLIENT + clientID);
        if (clientInfo == null || clientInfo.size() == 0) {
            throw new MyBaseException("客户不存在", InterfaceExceptionDict.RETURN_STATUS_CLIENTID_ERROR);
        }
        System.err.println(clientInfo);
        //校验用户的 ip ,根据客户的 id 找到他允许的 ip 是什么,然后和当前请求的 ip 进行比较(客户允许的 ip 应该是可以设置多个的)
        String ipaddress = (String) clientInfo.get("ipaddress");
        //实际上客户允许的 ip 是多个,我们可以使用一个字符串来表示多个 ip  比如 ip1,ip2,我们只要判断用户现在用的 ip 包不包含在允许里面
        if (StringUtils.isEmpty(ipaddress) || !ipaddress.contains(map.get("ip").toString())) {
            //不属于用户允许的 ip 范围
            throw new MyBaseException("ip 不允许", InterfaceExceptionDict.RETURN_STATUS_IP_ERROR);
        }
        //我们首先校验用户的身份是否是真实的,获取到客户的 id 和密码,然后和我们保存的密码进行比较
        //获取传递过来的账号和密码,然后和数据库中保存的账号和密码进行比较
        String pwd = (String) clientInfo.get("pwd");//从缓存中获取到用户的密码

        //用户我们的接口中传递的密码是可能是明文,所以我们需要转换,还有一种方式,我们要求用户在传递密码的时候传递的就是密文,不允许传递明文
        String userPwd = (String) map.get("pwd");//获取到用户传递的密码
        if (StringUtils.isEmpty(userPwd) || !userPwd.equalsIgnoreCase(pwd)) {
            throw new MyBaseException("密码错误", InterfaceExceptionDict.RETURN_STATUS_PWD_ERROR);
        }

        //我们对短信的内容长度是有要求的,因为短信内容不能太长,否则一条装不下,同时内容也不能为空
        String content = (String) map.get("content");
        if (StringUtils.isEmpty(content) || content.length() > 500) {//500 理论上应是可以通过管理平台修改的
            throw new MyBaseException("短信内容过长", InterfaceExceptionDict.RETURN_STATUS_MESSAGE_ERROR);
        }
        List<String> mobile = (List<String>) map.get("mobile");
        if (mobile == null || mobile.size() == 0) {//手机号没传递或者不满足一个手机号的长度
            throw new MyBaseException("手机号为空", InterfaceExceptionDict.RETURN_STATUS_MOBILE_ERROR);
        }

        //如果校验成功,是不是该发短信了?
        //短信要怎么发?我们要不要再校验一下每个手机号是不是符合要求?防止有非法的手机传递出来,这样后面的流程直接发短信(包括其他校验)即可
        //短信平台可以做的是发短信,包括短信验证码,包括一些其他的校验短信, 比如我们的客户的用户在客户的网站上面点了一个发送短信
        //客户的网站服务器会向我们发请求,其实我们服务器收到的是客户服务器发来的请求,我们没有必要让客户的服务器等我们返回结果,等有结果的时候我们告诉客户即可
        //所以我们的操作就可以是异步的,因为我们不需要即可返回结果
        //我们就可以在这边只负责判断手机号合法与否,其他的发送操作,更多的判断交给其他的功能
        //客户可能会调用我们的接口一次性给很多手机号发送短信,但是我们也不能无限制的允许手机号的数量,所以我们也有限制,比如一次性最多一千个
        //客户端传递传的手机号可能有错误的,我们需要筛选出来,理论上应该是正确的手机号发送短信,错误的手机号返回结果
        Map<String, List<String>> resultMap = checkMobile(mobile);//保存的是校验后的手机号
        List<String> successList = resultMap.get("success");//获取成功的
        // 假如说我们要判断正确的手机号
        if (successList.size() > 1000 || successList.size() == 0) {
            //这个数量我们可以通过管理平台定义,当然这个限制到底是正确的手机号的数量限制,还是不管正确与否传递过来的手机号的数量,这个实际上是业务上的需求,我们代码是根据需求来做的,需求要求判断正确的,我们就判断正确的
            throw new MyBaseException("手机号超出限制", InterfaceExceptionDict.RETURN_STATUS_MOBILE_ERROR);
        }
        //查询当前客户对应的通道的 id,在这里查询的原因是假设客户一次性发送一千条短信,我们也只需要查询一次,如果到了后面的网关,每条短信会被拆分为一条数据,需要查询一千次
        Object channelid = cacheService.hget(CacheConstants.CACHE_PREFIX_ROUTER + clientID, "channelid");
        //发送短信
        List<Standard_Submit> submitList = new ArrayList<>();
        for (String phone : successList) {
            Standard_Submit submit = new Standard_Submit();
            submit.setClientID(Integer.parseInt(clientID));
            submit.setDestMobile(phone);
            submit.setMessageContent(content);
            submit.setSendTime(new Date());
            int usertype = (int) clientInfo.get("usertype");
            submit.setSource(usertype);//用户的类型,如果是 http 的话后面我们还需要给用户推送状态报告
            submit.setSrcSequenceId(Integer.parseInt(srcId));//设置客户端侧的唯一序列号
            //设置通道的序列号
            submit.setGatewayID(Integer.parseInt(channelid.toString()));
            submitList.add(submit);

        }

        //
        LOGGER.error("准备发送手机号到下一个流程{}", submitList);
        //怎么发的问题,我们通过 mq 发出去
        pushSubmitQueueService.sendSmsToQueue(submitList);

        //还有一个错误的手机号没处理
        List<String> failList = resultMap.get("fail");//获取成功的
        if (failList.size() > 0) {
            //有错误的手机号
            //应该给客户返回错误信息
            throw new MyBaseExceptionWithResult("手机号不合法", InterfaceExceptionDict.RETURN_STATUS_MOBILE_ERROR, failList);

        }

    }

    /**
     * 将用户传递的手机号进行校验,按照正确和错误的方式进行分开存放到 map,
     *
     * @return
     */
    private Map<String, List<String>> checkMobile(List<String> mobiles) {

        List<String> success = new ArrayList<>();
        List<String> fail = new ArrayList<>();
        Map<String, List<String>> result = new HashMap<>();
        result.put("success", success);
        result.put("fail", fail);

        String regex = "^((13[0-9])|(14[579])|(15([0-3]|[5-9]))|(166)|(17[0135678])|(18[0-9])|(19[8|9]))\\d{8}$";
        for (String mobile : mobiles) {
            boolean matches = mobile.matches(regex);//判断手机号是否合法
            if (matches) {
                success.add(mobile);
            } else {
                fail.add(mobile);
            }
        }

        return result;
    }
}
